package com.fan.play.media.capture.audio

import android.Manifest
import android.media.AudioRecord
import android.media.MediaRecorder
import android.os.Process
import com.fan.play.media.capture.IAudioCallback
import com.fan.play.media.capture.ICapture
import com.fan.play.media.common.MediaConvertUtil
import com.fan.play.media.common.MediaDefine
import com.fan.play.utils.AppUtil
import com.fan.play.utils.LogUtil

/**
 * 使用AudioRecord采集音频
 *  1.简介：AudioRecord是安卓多媒体框架中用于录制音频的工具，它支持录制原始音频数据，即PCM数据，PCM数据不能被播放器直接
 *      播放，需要编码压缩成常见音频格式才能被播放器识别。
 *  2.录音流程
 *      1.配置参数
 *      2.初始化一个buffer，该buffer大于等于AudioRecord对象用于写声音数据的buffer大小
 *      3.AudioRecord对象
 *      4.录制相关操作
 *      5.建一个数据流，一边从AudioRecord中读取声音数据到初始化的buffer，一边将buffer中的数据导入数据流
 *      6.关闭数据流
 *      7.停止录制
 */
class AudioRecordCapture(
    private var mSampleRate: Int,
    private var mChannelNum: Int,
    private var mSampleBit: Int,
    private var mAudioCallback: IAudioCallback
) : ICapture {

    //音频采集线程
    private var mAudioCaptureThread: Thread? = null

    //音频采集对象
    private var mAudioRecord: AudioRecord? = null

    //音频采集口
    private var mAudioCapture = MediaRecorder.AudioSource.DEFAULT

    //音频采集Buffer
    private var mBufferSize = 0

    private var mRecording = false


    init {
        /**
         * getMinBufferSize用于获取成功创建AudioRecord对象所需的最小缓冲区大小，此大小不能保证在负载下能顺利录制，应
         * 根据预期的频率选择更高的值
         * sampleRateInHz:采样率
         * channelConfig:音频通道的配置
         * audioFormat:音频数据的格式
         */
        mBufferSize = AudioRecord.getMinBufferSize(
            mSampleRate,
            MediaConvertUtil.getChannelConfig(mChannelNum),
            MediaConvertUtil.getAudioFormat(mSampleBit)
        )
        mRecording = false
    }

    override fun start(): Boolean {
        LogUtil.logInfo("start audio capture")
        //检测AudioRecord的权限
        if (!AppUtil.checkPermission(Manifest.permission.RECORD_AUDIO)) {
            LogUtil.logInfo("permission denied. " + Manifest.permission.RECORD_AUDIO)
            return false
        }

        mAudioRecord = AudioRecord(
            mAudioCapture,
            mSampleRate,
            MediaConvertUtil.getChannelConfig(mChannelNum),
            MediaConvertUtil.getAudioFormat(mSampleBit),
            mBufferSize * 2
        )

        if (mAudioCaptureThread == null) {
            mAudioCaptureThread = Thread(mRecordRunnable)
        }

        mAudioCaptureThread!!.start()

        return true
    }

    override fun stop() {
        LogUtil.logInfo("stop audio capture")
        mRecording = false

        mAudioCaptureThread?.let { thread ->
            if (!thread.isInterrupted) {
                thread.interrupt()
            }
            thread.join()
        }
        mAudioCaptureThread = null
    }

    private val mRecordRunnable = Runnable {
        Process.setThreadPriority(Process.THREAD_PRIORITY_URGENT_AUDIO)
        mAudioRecord?.let { audioRecord ->
            try {
                if (audioRecord.state != AudioRecord.STATE_INITIALIZED)
                    return@Runnable

                audioRecord.startRecording()

                var buffer: Int
                val bytes = ByteArray(mBufferSize)
                while (mRecording) {
                    buffer = audioRecord.read(bytes, 0, mBufferSize)
                    if (buffer == AudioRecord.ERROR_INVALID_OPERATION ||
                        buffer == AudioRecord.ERROR_BAD_VALUE
                    ) {
                        continue
                    }

                    if (buffer == 0 || buffer == 1) {
                        break
                    }

                    mAudioCallback.onAudioFrame(bytes, MediaDefine.MediaRawType_S16)
                }
            } catch (e: Exception) {
                LogUtil.logError("AudioRecordCapture. $e")
            } finally {
                audioRecord.stop()
                audioRecord.release()
            }
        }
        mAudioRecord = null
    }


}